home *** CD-ROM | disk | FTP | other *** search
/ Delphi Informant Complete 1995 - 2000 / Delphi Informant Complete 1995 to 2000.iso / Delphi Informant Magazine Complete Works SOURCE CODE 1998.rar / 1998 / Nov / di9811gd / Example2 / Unit2.pas < prev   
Pascal/Delphi Source File  |  1998-04-25  |  9KB  |  293 lines

  1. unit Unit2;
  2.  
  3. interface
  4.  
  5. uses
  6.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  7.   StdCtrls;
  8.  
  9. type
  10.   { TLightThread }
  11.   {* For easy management of threads.                                          *}
  12.   {* Allows a thread to be "created" with a passed thread function. The       *}
  13.   {* function will exit cleanly when ThreadExiting is set to true, or         *}
  14.   {* "nastily" after a timeout of ThreadExitTimeout milliseconds.             *}
  15.   {* For the purposes of this example, though, we are pretty assured that the *}
  16.   {* ThreadFunc used will always exit cleanly... (How else to demonstrate     *}
  17.   {* a thread-safe DLL?)                                                      *}
  18.   TLightThread = class(TObject)
  19.   protected
  20.     FThreadHandle: THandle;
  21.     FThreadID: DWord;
  22.     FCS: TRTLCriticalSection;
  23.     FThreadExiting: Boolean;
  24.     FCallGetFirstWord,
  25.     FCallGetNextWord: Boolean;
  26.     function GetThreadExiting: Boolean;
  27.   public
  28.     constructor Create(ThreadFunc: TThreadFunc);
  29.     destructor Destroy; override;
  30.     property ThreadExiting: Boolean read GetThreadExiting;
  31.     property ThreadHandle: THandle read FThreadHandle;
  32.     property ThreadID: DWord read FThreadID;
  33.     property CallGetFirstWord: Boolean read FCallGetFirstWord
  34.                                        write FCallGetFirstWord;
  35.     property CallGetNextWord: Boolean read FCallGetNextWord
  36.                                        write FCallGetNextWord;
  37.   end;
  38.  
  39.   TGetWordProc = procedure(sz, szResult: PChar); stdcall;
  40.  
  41.   { TForm1 }
  42.   TForm1 = class(TForm)
  43.     btnLoad: TButton;
  44.     btnUnload: TButton;
  45.     lbThreads: TListBox;
  46.     Label1: TLabel;
  47.     btnNewThread: TButton;
  48.     btnCloseThread: TButton;
  49.     Label2: TLabel;
  50.     Edit1: TEdit;
  51.     btnGetFirstWord: TButton;
  52.     btnGetNextWord: TButton;
  53.     procedure FormCreate(Sender: TObject);
  54.     procedure FormDestroy(Sender: TObject);
  55.     procedure btnLoadClick(Sender: TObject);
  56.     procedure btnUnloadClick(Sender: TObject);
  57.     procedure btnNewThreadClick(Sender: TObject);
  58.     procedure btnCloseThreadClick(Sender: TObject);
  59.     procedure btnGetFirstWordClick(Sender: TObject);
  60.     procedure btnGetNextWordClick(Sender: TObject);
  61.   private
  62.     { Private declarations }
  63.   public
  64.     { Public declarations }
  65.     LibHandle: THandle;
  66.     ThreadList: TList;
  67.     procedure FreeLib;
  68.     procedure NewThread;
  69.     procedure CloseThread(Idx: Integer); { Close indexed thread. }
  70.     procedure CloseThreads;
  71.   end;
  72.  
  73. var
  74.   GetFirstWord: TGetWordProc;
  75.   GetNextWord: TGetWordProc;
  76.   Form1: TForm1;
  77.  
  78. const
  79.   ThreadSleepLength = 50; // 50 ms.
  80.   ThreadExitTimeout = 10000;
  81.  
  82. implementation
  83.  
  84. {$R *.DFM}
  85.  
  86. procedure ShowThreadMessage(Msg: String);
  87. begin
  88.   MessageBox(Form1.Handle, PChar(Msg), 'Message',
  89.              MB_OK or MB_SETFOREGROUND or MB_TASKMODAL);
  90. end;
  91.  
  92. function ThreadFunc(Parameter: Pointer): Integer;
  93. var
  94.   szResult: PChar;
  95. begin
  96.   while (not TLightThread(Parameter).ThreadExiting) do begin
  97.     with (TLightThread(Parameter)) do begin
  98.       {* This section of code is critical code--                              *}
  99.       {*   Because it accesses "global" memory, the thread should make sure   *}
  100.       {*   that it is the only thread trying to access this memory.           *}
  101.       {*   Obviously, I'm following bad programming practice right here, but  *}
  102.       {*   for the sake of the example, I have avoided using good programming *}
  103.       {*   technique because that's not the item being demonstrated...        *}
  104.       {*   Shoot me if you will...                                            *}
  105.       {*                                                                      *}
  106.       if CallGetFirstWord or CallGetNextWord then begin
  107.         GetMem(szResult, Length(Form1.Edit1.Text) + 1);
  108.         try
  109.           if CallGetFirstWord then
  110.             GetFirstWord(PChar(Form1.Edit1.Text), szResult)
  111.           else if CallGetNextWord then
  112.             GetNextWord(PChar(Form1.Edit1.Text), szResult);
  113.           ShowThreadMessage(String(szResult));
  114.           CallGetFirstWord := False;
  115.           CallGetNextWord := False;
  116.         finally
  117.           FreeMem(szResult, Length(Form1.Edit1.Text) + 1);
  118.         end;
  119.       end;
  120.     end;
  121.     Sleep(ThreadSleepLength);
  122.   end;
  123.   result := 0;
  124. end;
  125.  
  126. { TLightThread }
  127. constructor TLightThread.Create(ThreadFunc: TThreadFunc);
  128. begin
  129.   InitializeCriticalSection(FCS);
  130.   FThreadExiting := False;
  131.   try
  132.     FThreadHandle :=
  133.       BeginThread(nil, 0, ThreadFunc, Pointer(Self), 0, FThreadID);
  134.   except
  135.     on E: Exception do begin
  136.       DeleteCriticalSection(FCS);
  137.       raise;
  138.     end;
  139.   end;
  140. end;
  141.  
  142. destructor TLightThread.Destroy;
  143. begin
  144.   EnterCriticalSection(FCS);
  145.   try
  146.     FThreadExiting := True;
  147.   finally
  148.     LeaveCriticalSection(FCS);
  149.   end;
  150.   WaitForSingleObject(FThreadHandle, ThreadExitTimeout);
  151.   CloseHandle(FThreadHandle);
  152.   DeleteCriticalSection(FCS);
  153.   inherited;
  154. end;
  155.  
  156. function TLightThread.GetThreadExiting: Boolean;
  157. begin
  158.   EnterCriticalSection(FCS);
  159.   try
  160.     result := FThreadExiting;
  161.   finally
  162.     LeaveCriticalSection(FCS);
  163.   end;
  164. end;
  165.  
  166. { TForm1 }
  167.  
  168. procedure TForm1.FormCreate(Sender: TObject);
  169. begin
  170.   LibHandle := 0;
  171.   ThreadList := TList.Create;
  172. end;
  173.  
  174. procedure TForm1.FormDestroy(Sender: TObject);
  175. begin
  176.   FreeLib;                           // Free the library, if necessary
  177.   CloseThreads;
  178.   ThreadList.Free;                   // Free the list of threads.
  179. end;
  180.  
  181. procedure TForm1.btnLoadClick(Sender: TObject);
  182. begin
  183.   if LibHandle = 0 then
  184.     LibHandle := LoadLibrary('Dll2.dll');
  185.   if (LibHandle = 0) then
  186.     raise Exception.Create('Unable to load library.')
  187.   else begin
  188.     Edit1.Enabled := True;
  189.     btnGetFirstWord.Enabled := True;
  190.     btnGetNextWord.Enabled := True;
  191.     GetFirstWord := GetProcAddress(LibHandle, 'GetFirstWord');
  192.     GetNextWord := GetProcAddress(LibHandle, 'GetNextWord');
  193.   end;
  194. end;
  195.  
  196. procedure TForm1.btnUnloadClick(Sender: TObject);
  197. begin
  198.   try
  199.     FreeLib;
  200.     GetFirstWord := nil;
  201.     GetNextWord := nil;
  202.   finally
  203.     LibHandle := 0;
  204.     Edit1.Enabled := False;
  205.     btnGetFirstWord.Enabled := False;
  206.     btnGetNextWord.Enabled := False;
  207.   end;
  208. end;
  209.  
  210. procedure TForm1.btnNewThreadClick(Sender: TObject);
  211. begin
  212.   NewThread;
  213. end;
  214.  
  215. procedure TForm1.btnCloseThreadClick(Sender: TObject);
  216. begin
  217.   CloseThread(lbThreads.ItemIndex);
  218. end;
  219.  
  220. procedure TForm1.FreeLib;
  221. var
  222.   i, Cnt: Integer;
  223. begin
  224.   {* In comments is the appropriate way for a calling application
  225.      to free its library when it has multiple threads; however, for
  226.      the purpose of the example, we _just_ unload the library *}
  227.   FreeLibrary(LibHandle);
  228.   LibHandle := 0;
  229.   {if LibHandle <> 0 then begin
  230.     try
  231.       Cnt := ThreadList.Count;
  232.       for i := 0 to Cnt - 1 do CloseThread(0);
  233.       FreeLibrary(LibHandle);
  234.     finally
  235.       LibHandle := 0;
  236.     end;
  237.   end;}
  238. end;
  239.  
  240. procedure TForm1.NewThread;
  241. var
  242.   Thd: TLightThread;
  243. begin
  244.   { Create a thread }
  245.   Thd := TLightThread.Create(ThreadFunc);
  246.   { If thread was created successfully, then add the thread handle to
  247.     ThreadList, increment thread count and add an "identifier" to
  248.     the ListBox (for identification purposes only). }
  249.   ThreadList.Add(Pointer(Thd));
  250.   lbThreads.Items.Add('Thread #' + IntToStr(Thd.ThreadHandle));
  251.   lbThreads.ItemIndex := lbThreads.Items.Count - 1;
  252. end;
  253.  
  254. procedure TForm1.CloseThread(Idx: Integer);
  255. begin
  256.   if (Idx >= 0) and (Idx < ThreadList.Count) then begin
  257.     TLightThread(ThreadList.Items[Idx]).Free;
  258.     ThreadList.Delete(Idx);  ThreadList.Pack;
  259.     lbThreads.Items.Delete(Idx);
  260.     if (Idx = ThreadList.Count) then
  261.       lbThreads.ItemIndex := Idx - 1
  262.     else
  263.       lbThreads.ItemIndex := Idx;
  264.   end;
  265. end;
  266.  
  267. procedure TForm1.CloseThreads;
  268. var
  269.   i, Cnt: Integer;
  270. begin
  271.   Cnt := ThreadList.Count;
  272.   for i := 0 to Cnt - 1 do CloseThread(0);
  273. end;
  274.  
  275. procedure TForm1.btnGetFirstWordClick(Sender: TObject);
  276. begin
  277.   if Assigned(GetFirstWord) and (lbThreads.ItemIndex > -1) then
  278.     TLightThread(ThreadList.Items[lbThreads.I